#include "DbPart.h"
#include "connectpool.h"
#include <sstream>
#include <vector>
#include <set>
#include "Game.h"
#include "Logic.h"
#include "RedisClient.h"
#include "util.h"


char g_szSQL[2056]={0};
#define MAX_SQL_BUF_LEN (sizeof(g_szSQL)-1)

#define TABLE_NUMBER 100     //创建表个数
std::string tabelName_api_order = "";

CDbPart::CDbPart()
{
	m_dbHandler = NULL;
}

CDbPart::~CDbPart()
{
}

//获取当前日期
std::string CDbPart::GetDayDateString(time_t now, std::string delimiter, int64 uid)
{
    // 将当前时间转换为tm结构体
    tm* localTime = localtime(&now);

    // 获取当前年份
    int year = localTime->tm_year + 1900;

    // 获取当前月份
    int month = localTime->tm_mon + 1;

    // 获取当前日期
    int day = localTime->tm_mday;

    char cStr[64] = "";
    std::string str = "";

    if (uid < 0)
    {
        sprintf(cStr, "%d%s%02d%s%02d", year, delimiter.c_str(), month, delimiter.c_str(), day);
    }
    else
    {
        uint32 mode_uid = uid % TABLE_NUMBER;
        sprintf(cStr, "%d%s%02d%s%02d%s%d", year, delimiter.c_str(), month, delimiter.c_str(), day, delimiter.c_str(), mode_uid);
    }
    
    str = cStr;

    log_debug("GetDayDateString time=%s", str.c_str());
    return str;
}
std::string CDbPart::GetCurDayDateString(std::string delimiter, int64 uid)
{
    return GetDayDateString(time(NULL), delimiter, uid);
}

//看表是否存在
bool CDbPart::MysqlTabelExist(const std::string& tabel)
{
    if (tabel.size() <= 0)
    {
        log_debug("MysqlTabelExist tabelname="" return");
        return false;
    }

    std::string strSql = "SHOW TABLES LIKE '";
    strSql.append(tabel.c_str());
    strSql.append("';");

    bool ret = m_dbHandler->m_pStore->Query(strSql);
    if (ret == false)
    {
        log_error("MysqlTabelExist failed What=%s SQL=%s", m_dbHandler->m_pMysqlConn->What().c_str(), strSql.c_str());
        return false;
    }

    if (m_dbHandler->m_pStore->RowCount() <= 0)
    {
        log_debug("MysqlTabelExist tabel:%s exist return", tabel.c_str());
        return false;
    }

    return true;
}

//创建MYSQL表
void CDbPart::CreateApiOrderExceptionTabel(const std::string& tabel)
{
    char cSql[] = "`  (\
        `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,\
        `uid` int(10) UNSIGNED DEFAULT NULL,\
        `username` varchar(255) DEFAULT NULL,\
        `agency` varchar(255) DEFAULT NULL,\
        `token` varchar(255) DEFAULT NULL,\
        `platform` varchar(255) DEFAULT NULL,\
        `currency` varchar(255) DEFAULT NULL,\
        `op` int(10) UNSIGNED DEFAULT NULL,\
        `gameid` int(10) UNSIGNED DEFAULT NULL,\
        `bet` bigint(20) DEFAULT NULL,\
        `winlose` bigint(20) DEFAULT NULL,\
        `preserve` bigint(20) DEFAULT NULL,\
        `url` longtext COLLATE utf8mb4_general_ci DEFAULT NULL,\
        `req_id` varchar(255) DEFAULT NULL,\
        `resp_id` bigint(20) UNSIGNED DEFAULT NULL,\
        `req` longtext COLLATE utf8mb4_general_ci DEFAULT NULL,\
        `resp` longtext COLLATE utf8mb4_general_ci DEFAULT NULL,\
        `start_time` int(10) UNSIGNED DEFAULT NULL,\
        `update_time` int(10) UNSIGNED DEFAULT NULL,\
        `err_code` int(10) DEFAULT NULL,\
        `state` int(10) DEFAULT NULL,\
        `cycle` bigint(20) UNSIGNED DEFAULT NULL,\
        `orderid` bigint(20) UNSIGNED DEFAULT NULL,\
        `flag` int(10) DEFAULT NULL,\
        `ext` blob DEFAULT NULL,\
        PRIMARY KEY (`id`),\
        KEY `uid` (`uid`) USING BTREE,\
        KEY `start_time` (`start_time`) USING BTREE,\
        KEY `req_id` (`req_id`) USING BTREE,\
        KEY `state` (`state`) USING BTREE,\
        KEY `uuid` (`uid`,`cycle`,`orderid`,`op`) USING BTREE,\
        KEY `flag` (`flag`) USING BTREE\
        ) ENGINE = InnoDB AUTO_INCREMENT = 0 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '游戏订单记录表' ROW_FORMAT = DYNAMIC; ";

    memset(g_szSQL, 0, sizeof(g_szSQL));
    std::string strsql = "";
    strsql.append("CREATE TABLE `");
    strsql.append(tabel.c_str());
    strsql.append(cSql);

    strcat(g_szSQL, strsql.c_str());
    bool ret = m_dbHandler->m_pStore->Query(g_szSQL);
    if (ret == false) {
        log_error("CreateApiOrderExceptionTabel failed What=%s g_szSQL=%s", m_dbHandler->m_pMysqlConn->What().c_str(), g_szSQL);
        return;
    }
    log_debug("CreateApiOrderExceptionTabel creator tabel return %s", strsql.c_str());
}

bool CDbPart::CheckApiOrderExceptionTabel(const std::string& tabel, bool just_check)
{
    static std::vector<std::string> table_cache;

    bool state = false;
    if (table_cache.end() != std::find(table_cache.begin(), table_cache.end(), tabel))
    {
        state = true;
    }
    else
    {        
        //先判断表是否存在
        if (MysqlTabelExist(tabel))
        {
            table_cache.push_back(tabel);
            state = true;
        }
        else if (just_check)
        {
            state = false;
        }
        else
        {
            CreateApiOrderExceptionTabel(tabel);
            table_cache.push_back(tabel);
            state = true;
        }
    }

    // 最多保留最近30条缓存
    if (table_cache.size() > 30)
    {
        table_cache.erase(table_cache.begin());
    }

    return true;
}

bool CDbPart::CreateApiOrderException(const api_order::ApiOrder& api_order)
{
    std::string strTabelName = "log_api_order_exception_" + GetDayDateString(api_order.start_time(), "_");
    CheckApiOrderExceptionTabel(strTabelName);
    // log_debug("CreateApiOrderException tabelname=%s", strTabelName.c_str());

	std::stringstream sql;
	sql << "INSERT INTO " << strTabelName;
	sql << " (uid, username, agency, token, platform, currency, gameid, bet,winlose,preserve, op, url, req_id, resp_id, req, resp, start_time, update_time,state,err_code,cycle,orderid,flag) values (";
	sql << api_order.uid() << ",";
	sql << "'" << api_order.username() << "',";
	sql << "'" << api_order.agency() << "',";
	sql << "'" << EscapeString(api_order.token()) << "',";
	sql << "'" << EscapeString(api_order.platform()) << "',";
	sql << "'" << api_order.currency() << "',";
	sql << api_order.gameid() << ",";
	sql << api_order.bet() << ",";
	sql << api_order.winlose() << ",";
	sql << api_order.preserve() << ",";
	sql << api_order.op() << ",";
	sql << "'" << api_order.url() << "',";
	sql << "'" << api_order.req_id() << "',";
	sql << api_order.resp_id() << ",";
	sql << "'" << EscapeString(api_order.req_body()) << "',";
	sql << "'" << EscapeString(api_order.resp_body()) << "',";
	sql << api_order.start_time() << ",";
	sql << api_order.update_time() << ",";
	sql << api_order.state() << ",";
	sql << api_order.err_code() << ",";
	sql << api_order.cycle() << ",";
	sql << api_order.orderid() << ",";
	sql << 0;
	sql << ")";

    log_debug("sql=%s", sql.str().c_str());
    bool ret = m_dbHandler->UpdateSql(sql.str().c_str());
    if (ret == false)
    {
        log_error("CreateApiOrderException insert into uid=%u What=%s g_szSQL=%s", api_order.uid(), m_dbHandler->m_pMysqlConn->What().c_str(), sql.str().c_str());
        return false;
    }
    return true;
}

bool CDbPart::BatchCreateApiOrderException(const std::vector<api_order::ApiOrder>& api_order_group)
{
    std::map<std::string, std::stringstream> sql_values;
    for (auto it = api_order_group.begin(); it != api_order_group.end(); it++)
    {
        const api_order::ApiOrder& api_order = *it;
        std::string strTabelName = "log_api_order_exception_" + GetDayDateString(api_order.start_time(), "_");
        std::stringstream& sql_value = sql_values[strTabelName];
        if (!sql_value.str().empty()) sql_value << ",";
        sql_value << "(" << api_order.uid() << ",";
        sql_value << "'" << api_order.username() << "',";
        sql_value << "'" << api_order.agency() << "',";
        sql_value << "'" << api_order.token() << "',";
        sql_value << "'" << api_order.platform() << "',";
        sql_value << "'" << api_order.currency() << "',";
        sql_value << api_order.gameid() << ",";
        sql_value << api_order.bet() << ",";
        sql_value << api_order.winlose() << ",";
        sql_value << api_order.preserve() << ",";
        sql_value << api_order.op() << ",";
        sql_value << "'" << api_order.url() << "',";
        sql_value << "'" << api_order.req_id() << "',";
        sql_value << api_order.resp_id() << ",";
        sql_value << "'" << api_order.req_body() << "',";
        sql_value << "'" << EscapeString(api_order.resp_body()) << "',";
        sql_value << api_order.start_time() << ",";
        sql_value << api_order.update_time() << ",";
        sql_value << api_order.state() << ",";
        sql_value << api_order.err_code() << ",";
        sql_value << api_order.cycle() << ",";
        sql_value << api_order.orderid() << ",";
        sql_value << 0;
        sql_value << ")";
    }

    bool is_ok = true;
    for (auto& item : sql_values)
    {
        const std::string& strTabelName = item.first;
        CheckApiOrderExceptionTabel(strTabelName);
        // log_debug("tabelname=%s", strTabelName.c_str());

        std::stringstream sql;
        sql << "INSERT INTO " << strTabelName;
        sql << " (uid, username, agency, token, platform, currency, gameid, bet,winlose,preserve, op, url, req_id, resp_id, req, resp, start_time, update_time,state,err_code,cycle,orderid,flag) values ";
        sql << item.second.str();

        log_debug("sql=%s", sql.str().c_str());
        bool ret = m_dbHandler->UpdateSql(sql.str().c_str());
        if (ret == false)
        {
            log_error("batch insert failed, What=%s g_szSQL=%s", m_dbHandler->m_pMysqlConn->What().c_str(), sql.str().c_str());
            is_ok = false;
        }
    }
    return is_ok;
}

bool CDbPart::FinishApiOrderException(const api_order::ApiOrder& api_order, std::int32_t flag, std::int32_t trade_time)
{
    bool ret = false;
    if (trade_time != 0)
    {
        ret |= FinishApiOrderExceptionByTime(api_order, flag, trade_time);
    }
    else
    {
        ret |= FinishApiOrderExceptionByTime(api_order, flag, api_order.start_time());
        ret |= FinishApiOrderExceptionByTime(api_order, flag, api_order.start_time() - 86400);
    }
    return ret;
}

bool CDbPart::FinishApiOrderExceptionByTime(const api_order::ApiOrder& api_order, std::int32_t flag, std::int32_t trade_time)
{
    std::string strTabelName = "log_api_order_exception_" + GetDayDateString(trade_time, "_");
    if (!CheckApiOrderExceptionTabel(strTabelName, true))
    {
        return false;
    }
    log_debug("FinishApiOrderExceptionByTime tabelname=%s", strTabelName.c_str());

	memset(g_szSQL, 0, sizeof(g_szSQL));
	snprintf(g_szSQL, sizeof(g_szSQL)-1, "UPDATE %s set flag = %d where uid = %u and cycle = %d and orderid = %d ", 
        strTabelName.c_str(), flag, api_order.uid(), api_order.cycle(), api_order.orderid());
	log_debug("FinishApiOrderExceptionByTime-> SQL=%s", g_szSQL);
	bool RetExc2 = m_dbHandler->UpdateSql(g_szSQL);
	if (!RetExc2)
	{
		log_error("RetExc is falied RetExc2=%d", RetExc2);
		return false;
	}
	return true;
}

bool CDbPart::createApiOrderExceptionTable(const std::string& tabel)
{
	ostringstream ossSQL;
	ossSQL << "CREATE TABLE IF NOT EXISTS `" << tabel << "`(\
        `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,\
        `uid` int(10) UNSIGNED DEFAULT NULL,\
        `username` varchar(255) DEFAULT NULL,\
        `agency` varchar(255) DEFAULT NULL,\
        `token` varchar(255) DEFAULT NULL,\
        `platform` varchar(255) DEFAULT NULL,\
        `currency` varchar(255) DEFAULT NULL,\
        `op` int(10) UNSIGNED DEFAULT NULL,\
        `gameid` int(10) UNSIGNED DEFAULT NULL,\
        `bet` bigint(20) DEFAULT NULL,\
        `winlose` bigint(20) DEFAULT NULL,\
        `preserve` bigint(20) DEFAULT NULL,\
        `url` longtext COLLATE utf8mb4_general_ci DEFAULT NULL,\
        `req_id` varchar(255) DEFAULT NULL,\
        `resp_id` bigint(20) UNSIGNED DEFAULT NULL,\
        `req` longtext COLLATE utf8mb4_general_ci DEFAULT NULL,\
        `resp` longtext COLLATE utf8mb4_general_ci DEFAULT NULL,\
        `start_time` int(10) UNSIGNED DEFAULT NULL,\
        `update_time` int(10) UNSIGNED DEFAULT NULL,\
        `err_code` int(10) DEFAULT NULL,\
        `state` int(10) DEFAULT NULL,\
        `cycle` bigint(20) UNSIGNED DEFAULT NULL,\
        `orderid` bigint(20) UNSIGNED DEFAULT NULL,\
        `flag` int(10) DEFAULT NULL,\
        `ext` blob DEFAULT NULL,\
        PRIMARY KEY (`id`),\
        KEY `uid` (`uid`) USING BTREE,\
        KEY `start_time` (`start_time`) USING BTREE,\
        KEY `req_id` (`req_id`) USING BTREE,\
        KEY `state` (`state`) USING BTREE,\
        KEY `uuid` (`uid`,`cycle`,`orderid`,`op`) USING BTREE,\
        KEY `flag` (`flag`) USING BTREE\
        ) ENGINE = InnoDB AUTO_INCREMENT = 0 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '游戏订单记录表' ROW_FORMAT = DYNAMIC";

	if (!m_dbHandler->UpdateSql(ossSQL.str().c_str()))
	{
		m_dbHandler->m_strError = m_dbHandler->m_pMysqlConn->What();
		log_error("createApiOrderExceptionTable err: %s\r sql=%s \n", m_dbHandler->m_pMysqlConn->What().c_str(), ossSQL.str().c_str());

		return false;
	}

    return true;
}

bool CDbPart::addApiOrderException(const std::string& tabel, const api_order::ApiOrder& api_order)
{
    // std::string ext;
    // if (CTools::ToJson(ext, api_order))
    // {
    //     std::stringstream ss;
    //     ss << std::hex << std::uppercase << std::setfill('0');
    //     for (std::string::const_iterator it = ext.begin(); it != ext.end(); ++it)
    //     {
    //         ss << std::setw(2) << static_cast<unsigned int>(static_cast<unsigned char>(*it));
    //     }
    //     ext = "x\'" + ext + "\'";
    // }
    // else
    // {
    //     ext = "";
    // }
	ostringstream sql;
	sql << "INSERT INTO " << tabel;
	sql << " (uid, username, agency, token, platform, currency, gameid, bet,winlose,preserve, op, url, req_id, resp_id, req, resp, start_time, update_time,state,err_code,cycle,orderid,flag) values (";
	// sql << " (uid, username, agency, token, platform, currency, gameid, bet,winlose,preserve, op, url, req_id, resp_id, req, resp, start_time, update_time,state,err_code,cycle,orderid,ext,flag) values (";
	sql << api_order.uid() << ",";
	sql << "'" << api_order.username() << "',";
	sql << "'" << api_order.agency() << "',";
	sql << "'" << api_order.token() << "',";
	sql << "'" << api_order.platform() << "',";
	sql << "'" << api_order.currency() << "',";
	sql << api_order.gameid() << ",";
	sql << api_order.bet() << ",";
	sql << api_order.winlose() << ",";
	sql << api_order.preserve() << ",";
	sql << api_order.op() << ",";
	sql << "'" << api_order.url() << "',";
	sql << "'" << api_order.req_id() << "',";
	sql << api_order.resp_id() << ",";
	sql << "'" << api_order.req_body() << "',";
	sql << "'" << EscapeString(api_order.resp_body()) << "',";
	sql << api_order.start_time() << ",";
	sql << api_order.update_time() << ",";
	sql << api_order.state() << ",";
	sql << api_order.err_code() << ",";
	sql << api_order.cycle() << ",";
	sql << api_order.orderid() << ",";
	// sql << ext << ",";
	sql << 0;
	sql << ")";

	log_debug("sql=%s", sql.str().c_str());
	if (!m_dbHandler->UpdateSql(sql.str().c_str()))
	{
		log_error("addApiOrderException insert into uid=%u What=%s g_szSQL=%s", api_order.uid(), m_dbHandler->m_pMysqlConn->What().c_str(), sql.str().c_str());
		return false;
	}

	return true;
}

bool CDbPart::finishApiOrderException(const std::string& tabel, const api_order::ApiOrder& api_order, std::int32_t flag)
{
	memset(g_szSQL, 0, sizeof(g_szSQL));
	snprintf(g_szSQL, sizeof(g_szSQL) - 1, "UPDATE %s set flag = %d where uid = %u and cycle = %lld and orderid = %lld ",
        tabel.c_str(), flag, api_order.uid(), api_order.cycle(), api_order.orderid());
	log_debug("FinishApiOrderExceptionByTime-> SQL=%s", g_szSQL);
	if (!m_dbHandler->UpdateSql(g_szSQL))
	{
		m_dbHandler->m_strError = m_dbHandler->m_pMysqlConn->What();
		log_error("createApiOrderExceptionTable err: %s\r sql=%s \n", m_dbHandler->m_pMysqlConn->What().c_str(), g_szSQL);
		return false;
	}

	return true;
}

std::string CDbPart::EscapeString(const std::string& input){
    std::vector<char> buffer;
    buffer.resize(input.length()*2+2);
    memset(buffer.data(), 0, buffer.size());
    int result = mysql_real_escape_string(m_dbHandler->m_pStore->GetMySqlConn(), buffer.data(), input.data(), input.length());
    if( result > 0 ){
        return std::string(buffer.data(), result);
    }
    return input;
}


